package com.sailfish.template.infra.config;

import com.sailfish.template.infra.common.constants.ResponseCode;
import com.sailfish.template.infra.exception.AppCommException;
import com.sailfish.template.infra.exception.AppCommExceptionResponse;
import com.sailfish.template.infra.exception.ExceptionType;
import com.sailfish.template.infra.exception.FeignClientException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.HttpClientErrorException;

import javax.validation.UnexpectedTypeException;
import java.util.Locale;

/**
 * @author XIAXINYU3
 * @date 2021.11.1
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    @Autowired
    MessageSource messageSource;

    public GlobalExceptionHandler() {
        log.info("初始化全局异常......");
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<AppCommExceptionResponse> process(MethodArgumentNotValidException exception) {
        String message = null;
        String code = "common.param.error";
        try {
            code = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage();
            message = messageSource.getMessage(code, null, locale());
        } catch (Exception e) {
            log.error("exception process get massage exception {}", exception, e);
        }
        message = message != null ? message : code;

        ExceptionType exceptionType = ExceptionType.REQUEST_PARAM_ERROR;
        log.error("请求参数异常: name={}, message={}", exceptionType.name(), exceptionType.getErrorMessage(), exception);
        return new ResponseEntity<>(
                new AppCommExceptionResponse(true, ResponseCode.EXCEPTION, exceptionType.getErrorMessage() + ": " + message),
                HttpStatus.OK);
    }

    @ExceptionHandler(UnexpectedTypeException.class)
    public ResponseEntity<AppCommExceptionResponse> process(UnexpectedTypeException exception) {
        ExceptionType exceptionType = ExceptionType.REQUEST_PARAM_ERROR;
        log.error("请求参数异常: name={}, message={}", exceptionType.name(), exceptionType.getErrorMessage(), exception);
        return new ResponseEntity<>(
                new AppCommExceptionResponse(true, ResponseCode.EXCEPTION, exceptionType.getErrorMessage()),
                HttpStatus.OK);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity<AppCommExceptionResponse> process(MissingServletRequestParameterException exception) {
        ExceptionType exceptionType = ExceptionType.REQUEST_PARAM_MISSING;
        log.error("请求参数异常: name={}, message={}", exceptionType.name(), exceptionType.getErrorMessage(), exception);
        return new ResponseEntity<>(
                new AppCommExceptionResponse(true, ResponseCode.EXCEPTION, exceptionType.getErrorMessage()),
                HttpStatus.OK);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<AppCommExceptionResponse> process(HttpRequestMethodNotSupportedException exception) {
        ExceptionType exceptionType = ExceptionType.REQUEST_METHOD_NOT_SUPPORTED;
        log.error("请求参数异常: name={}, message={}", exceptionType.name(), exceptionType.getErrorMessage(), exception);
        return new ResponseEntity<>(
                new AppCommExceptionResponse(true, ResponseCode.EXCEPTION, exceptionType.getErrorMessage()),
                HttpStatus.OK);
    }

    @ExceptionHandler(BadSqlGrammarException.class)
    public ResponseEntity<AppCommExceptionResponse> process(BadSqlGrammarException exception) {
        ExceptionType exceptionType = ExceptionType.DB_BAD_SQL_GRAMMAR;
        log.error("数据库异常: name={}, message={}", exceptionType.name(), exceptionType.getErrorMessage(), exception);
        return new ResponseEntity<>(
                new AppCommExceptionResponse(true, ResponseCode.EXCEPTION, exceptionType.getErrorMessage()),
                HttpStatus.OK);
    }

    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity<AppCommExceptionResponse> process(NullPointerException exception) {
        ExceptionType exceptionType = ExceptionType.SYS_NULL_POINT;
        log.error("空指针异常: name={}, message={}", exceptionType.name(), exceptionType.getErrorMessage(), exception);

        return new ResponseEntity<>(
                new AppCommExceptionResponse(true, ResponseCode.EXCEPTION, exceptionType.getErrorMessage()),
                HttpStatus.OK);
    }

    @ExceptionHandler(HttpClientErrorException.class)
    public ResponseEntity<AppCommExceptionResponse> process(HttpClientErrorException exception) {
        ExceptionType exceptionType = ExceptionType.SYS_NULL_POINT;
        log.error("请求远程服务异常msg", exception);
        return new ResponseEntity<>(
                new AppCommExceptionResponse(true, ResponseCode.EXCEPTION, exceptionType.getErrorMessage()),
                HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(FeignClientException.class)
    public ResponseEntity<AppCommExceptionResponse> process(FeignClientException exception) {
        log.error("调用远程服务异常", exception);
        ExceptionType exceptionType = ExceptionType.REQUEST_SERVER_ERROR;

        String message = null;
        try {
            String code = StringUtils.isEmpty(exception.getCode()) ? "common.feign.error" : exception.getCode();
            message = messageSource.getMessage(exception.getCode(), exception.getParameters(), locale());
        } catch (Exception e) {
            log.error("exception message {}", exception, e);
        }

        return new ResponseEntity<>(new AppCommExceptionResponse(true, ResponseCode.EXCEPTION,
                !StringUtils.isEmpty(message) ? message : exceptionType.getErrorMessage()), HttpStatus.OK);
    }

    @ExceptionHandler(AppCommException.class)
    public ResponseEntity<AppCommExceptionResponse> process(AppCommException exception) {
        String message = null;
        try {
            message = messageSource.getMessage(exception.getCode(), exception.getParameters(), locale());
        } catch (Exception e) {
            log.error("exception message {}", exception, e);
        }
        return new ResponseEntity<>(new AppCommExceptionResponse(true, ResponseCode.EXCEPTION,
                !StringUtils.isEmpty(message) ? message : exception.getMessage()), HttpStatus.OK);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<AppCommExceptionResponse> process(Exception exception) {
        log.error("拦截未指定的异常", exception);
        ExceptionType exceptionType = ExceptionType.SYS_BUSINESS_ERROR;
        return new ResponseEntity<>(
                new AppCommExceptionResponse(true, ResponseCode.EXCEPTION, exceptionType.getErrorMessage()),
                HttpStatus.OK);
    }

    /**
     * 返回用户的语言类型
     *
     * @return Locale
     */
    private Locale locale() {
        Locale locale = Locale.SIMPLIFIED_CHINESE;
        return locale;
    }
}
